package se.webbzon.boltzmann.battle.npc;

import javax.vecmath.Point3d;

import se.webbzon.boltzmann.battle.object.npc.BattleNPC;
import se.webbzon.boltzmann.game.character.CharacterActionEnum;
import se.webbzon.boltzmann.game.character.CharacterFacing;
import se.webbzon.boltzmann.npc.AbstractNPC;
import se.webbzon.boltzmann.npc.NPCDuty;

public class DefendingDuty extends NPCDuty {
	
	// The priority of this duty
	private static final int priority = 0;
	
	// The minimal number of steps to wait between moving
	// to a new position
	private static final int waitSteps = 60;
		
	// The default walking range from the start position
	private static final double defaultRange = 32;
	
	// The facing of the character
	private final CharacterFacing facing;

	// The maximal walking range from the start position
	private final double range;
		
	// The start position of the non-player character when
	// this duty was assigned.
	private final Point3d startPosition;
		
	// The target destination
	private final Point3d target;
		
	// A counter
	private int counter;
	
	/*============================================================
	Constructors
	============================================================*/

	/** Creates a new defending duty for a non-player battle character. **/
	public DefendingDuty(BattleNPC npc, CharacterFacing facing) {
		this(npc, facing, defaultRange);
	}
	
	/** Creates a new defending duty for a non-player battle character. 
	 * The maximal range this character will move is given by the input. **/
	public DefendingDuty(	BattleNPC npc, 
							CharacterFacing facing, 
							double range) {
		super(priority,npc);
		this.facing = facing;
		this.range = range;
		startPosition = new Point3d();
		target = new Point3d();
	}
	
	/*============================================================
	Public Methods
	============================================================*/
	
	/** Returns the maximal walking range from the start position. **/
	public double getRange() {
		return range;
	}
	
	/** Copies the start position of this duty to the given Point3d.**/
	public void getStartPosition(Point3d startPosition) {
		startPosition.set(startPosition);
	}
	
	/** Sets the walking target for this duty together with a motion speed. **/
	public void setTarget(double x, double z, double speed) {
		if (target.x != x || target.z != z) {
			target.set(x,target.y,z);
			AbstractNPC npc = getNPC();
			final double angle = Math.atan2(npc.getZ() - z,
											x - npc.getX());
			npc.setVelocity(speed * Math.cos(angle),0,-speed * Math.sin(angle));
			npc.setAction(CharacterActionEnum.WALK);
			counter = -1;
		}
	}
	
	/** This method is invoked when the the non-player character associated
	 * with this duty reaches the target. **/
	public void onReachTarget() {
		AbstractNPC npc = getNPC();
		npc.setVelocity(0, 0, 0);
		counter = waitSteps + (int) (waitSteps * Math.random());
		npc.setAction(CharacterActionEnum.STAND);
	}
	
	@Override public void onBegin() {
		AbstractNPC npc = getNPC();
		npc.getPosition(startPosition);
		counter = waitSteps;
		npc.setAction(CharacterActionEnum.STAND);
		npc.setFacing(facing);
	}

	@Override public void onStep() {
		AbstractNPC npc = getNPC();
		
		if (counter > 0)
			// Wait for a bit
			counter--;
		
		else if (counter == 0) {
			setTarget(	startPosition.x + range*(Math.random()-0.5),
						startPosition.z + range*(Math.random()-0.5),
						npc.getWalkSpeed());
		} else {
			// Check if the non-player character has reached the target destination
			final double dx = target.x - npc.getX();
			final double dz = target.z - npc.getZ();
			if (-2 < dx && dx < 2 && -2 < dz && dz < 2)
				onReachTarget();
		}
	}

	@Override public void onEnd() {
		getNPC().setVelocity(0, 0, 0);
	}

}
